using System;
using System.Globalization;
using System.IO;
using ICSharpCode.SharpZipLib.Zip;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Upgrade;

namespace Lapointe.WebPartPageHistory
{
    internal class CommonUtilities
    {
        /// <summary>
        /// Gets the folder within a list.  If the folder does not exist it is created.
        /// </summary>
        /// <param name="targetWeb">The target web.</param>
        /// <param name="targetList">The target list.</param>
        /// <param name="folderUrl">The folder URL.</param>
        /// <returns></returns>
        internal static SPFolder GetFolder(SPWeb targetWeb, SPList targetList, string folderUrl)
        {
            if (string.IsNullOrEmpty(folderUrl))
                return targetList.RootFolder;

            SPFolder folder = targetWeb.GetFolder(targetList.RootFolder.Url + "/" + folderUrl);

            if (!folder.Exists)
            {
                if (!targetList.EnableFolderCreation)
                {
                    targetList.EnableFolderCreation = true;
                    targetList.Update();
                }

                // We couldn't find the folder so create it
                string[] folders = folderUrl.Trim('/').Split('/');

                string folderPath = string.Empty;
                for (int i = 0; i < folders.Length; i++)
                {
                    folderPath += "/" + folders[i];
                    folder = targetWeb.GetFolder(targetList.RootFolder.Url + folderPath);
                    if (!folder.Exists)
                    {
                        SPListItem newFolder = targetList.Items.Add("", SPFileSystemObjectType.Folder, folderPath.Trim('/'));
                        newFolder.Update();
                        folder = newFolder.Folder;
                    }
                }
            }
            // Still no folder so error out
            if (folder == null)
                throw new SPException(string.Format("The folder '{0}' could not be found.", folderUrl));
            return folder;
        }

        /// <summary>Returns an elevated site</summary>
        /// <param name="theSite">
        /// The site that you want an elevated instance of. 
        /// You must dispose of this object unless it is part of SPContext.Current.
        /// </param>
        /// <returns>An elevated site context.</returns>
        /// <remarks>Be sure to dispose of objects created from this method.</remarks>
        public static SPSite GetElevatedSite(SPSite theSite)
        {
            var sysToken = GetSystemToken(theSite);
            return new SPSite(theSite.ID, sysToken);
        }

        /// <summary>Gets a UserToken for the system account.</summary>
        /// <param name="site"></param> 
        /// <returns>A usertoken for the system account user./returns>
        /// <remarks>Use this token to impersonate the system account</remarks>
        public static SPUserToken GetSystemToken(SPSite site)
        {
            site.CatchAccessDeniedException = false;
            try
            {
                return site.SystemAccount.UserToken;
            }
            catch (UnauthorizedAccessException)
            {
                SPUserToken sysToken = null;

                // Only use runwithelevated to grab the system user token.
                SPSecurity.RunWithElevatedPrivileges(
                    delegate
                    {
                        using (SPSite lolcatKiller = new SPSite(site.ID))
                        {
                            sysToken = lolcatKiller.SystemAccount.UserToken;
                        }
                    }
                );
                return sysToken;
            }
        }


        /// <summary>
        /// Gets the list by URL.
        /// </summary>
        /// <param name="web">The web.</param>
        /// <param name="listUrlName">Name of the list URL.</param>
        /// <returns></returns>
        internal static SPList GetListByUrl(SPWeb web, string listUrlName)
        {
            ConfirmNotNull(web, "web");
            ConfirmNotNullOrEmpty(listUrlName, "listUrlName");
            string strUrl = ConcatUrls(web.Url, listUrlName);
            return web.GetList(strUrl);
        }

        /// <summary>
        /// Confirms the not null.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="parameterName">Name of the parameter.</param>
        internal static void ConfirmNotNull(object value, string parameterName)
        {
            if (value == null)
            {
                throw new ArgumentNullException(parameterName);
            }
        }

        /// <summary>
        /// Confirms the not null or empty.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="parameterName">Name of the parameter.</param>
        internal static void ConfirmNotNullOrEmpty(string value, string parameterName)
        {
            if (string.IsNullOrEmpty(value))
            {
                throw new ArgumentNullException(parameterName);
            }
        }

        /// <summary>
        /// Confirms the not null or empty string.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="parameterName">Name of the parameter.</param>
        internal static void ConfirmNotNullOrEmptyString(string value, string parameterName)
        {
            if (string.IsNullOrEmpty(value))
            {
                throw new ArgumentException(string.Format("The parameter '{0}' is null or empty.", parameterName));
            }
        }

        /// <summary>
        /// Gets the unique list title.
        /// </summary>
        /// <param name="lists">The lists.</param>
        /// <param name="originalTitle">The original title.</param>
        /// <returns></returns>
        internal static string GetUniqueListTitle(SPListCollection lists, string originalTitle)
        {
            const string listName = "{0}{1}";
            if (GetListIfExists(lists, originalTitle) != null)
            {
                string uniqueListTitle;
                int num = 0;
                do
                {
                    num++;
                    uniqueListTitle = string.Format(CultureInfo.InvariantCulture, listName, new object[] { originalTitle, num.ToString(CultureInfo.InvariantCulture) });
                }
                while (GetListIfExists(lists, listName) != null);
                return uniqueListTitle;
            }
            return originalTitle;
        }

        /// <summary>
        /// Gets the list if it exists otherwise returns null.
        /// </summary>
        /// <param name="lists">The lists.</param>
        /// <param name="listName">Name of the list.</param>
        /// <returns></returns>
        internal static SPList GetListIfExists(SPListCollection lists, string listName)
        {
            SPList list = null;
            try
            {
                list = lists[listName];
            }
            catch (ArgumentException)
            {
                
            }
            return list;
        }

 

 


 

        internal static string GetUniqueObjectUrl(SPSite site, string desiredServerRelativeUrl, string fileExtension,
                                                  int maxRetry)
        {
            string uniqueUrl = null;
            if (SPManager.PeekIsUpgradeRunning())
            {
                uniqueUrl = GetUniqueObjectUrlHelper(site, desiredServerRelativeUrl, fileExtension, maxRetry);
            }
            else
            {
                Guid siteId = site.ID;
                SPSecurity.RunWithElevatedPrivileges(delegate
                                                         {
                                                             using (SPSite site1 = new SPSite(siteId))
                                                             {
                                                                 uniqueUrl =
                                                                     GetUniqueObjectUrlHelper(site1,
                                                                                              desiredServerRelativeUrl,
                                                                                              fileExtension, maxRetry);
                                                             }
                                                         });
            }
            return uniqueUrl;
        }


        /// <summary>
        /// Gets the unique object URL.
        /// </summary>
        /// <param name="site">The site.</param>
        /// <param name="desiredServerRelativeUrl">The desired server relative URL.</param>
        /// <param name="fileExtension">The file extension.</param>
        /// <param name="maxRetry">The max retry.</param>
        /// <returns></returns>
        private static string GetUniqueObjectUrlHelper(SPSite site, string desiredServerRelativeUrl,
                                                       string fileExtension, int maxRetry)
        {
            if (desiredServerRelativeUrl == null)
            {
                throw new ArgumentNullException("desiredServerRelativeUrl");
            }
            if (site == null)
            {
                throw new ArgumentNullException("site");
            }
            desiredServerRelativeUrl = desiredServerRelativeUrl.Trim();
            if (desiredServerRelativeUrl.EndsWith("/", StringComparison.OrdinalIgnoreCase))
            {
                throw new ArgumentException(
                    string.Format(CultureInfo.CurrentUICulture, "Parameter cannot end in slash.",
                                  new object[] {"desiredServerRelativeUrl"}));
            }
            int num = 0;
            string tempDesiredServerRelativeUrl = desiredServerRelativeUrl;
            bool noFileExtension = string.IsNullOrEmpty(fileExtension);

            try
            {
                if (!string.IsNullOrEmpty(fileExtension) && 
                    tempDesiredServerRelativeUrl.EndsWith(fileExtension, StringComparison.OrdinalIgnoreCase))
                {
                    tempDesiredServerRelativeUrl = 
                        tempDesiredServerRelativeUrl.Substring(0, tempDesiredServerRelativeUrl.Length - fileExtension.Length);
                }
                SPWeb web = site.OpenWeb();
                try
                {
                    while (web.GetObject(desiredServerRelativeUrl) != null)
                    {
                        if ((maxRetry >= 0 && num <= maxRetry) || maxRetry < 0)
                        {
                            num++;
                            desiredServerRelativeUrl = tempDesiredServerRelativeUrl + num.ToString(CultureInfo.InvariantCulture);
                            if (noFileExtension)
                            {
                                desiredServerRelativeUrl = desiredServerRelativeUrl + fileExtension;
                            }
                        }
                    }
                    return desiredServerRelativeUrl;
                }
                finally
                {
                    if (web != null)
                    {
                        web.Close();
                    }
                }
            }
            catch (FileNotFoundException)
            {
                return desiredServerRelativeUrl;
            }
        }


        /// <summary>
        /// Concats the urls.
        /// </summary>
        /// <param name="firstPart">The first part.</param>
        /// <param name="secondPart">The second part.</param>
        /// <returns></returns>
        internal static string ConcatUrls(string firstPart, string secondPart)
        {
            if (!firstPart.EndsWith("/", StringComparison.OrdinalIgnoreCase))
            {
                if (!secondPart.StartsWith("/"))
                {
                    return (firstPart + "/" + secondPart);
                }
                return (firstPart + secondPart);
            }
            if (secondPart.StartsWith("/"))
            {
                firstPart = firstPart.TrimEnd("/".ToCharArray());
            }
            return (firstPart + secondPart);
        }

        /// <summary>
        /// Unzips the file.
        /// </summary>
        /// <param name="historyItem">The history item.</param>
        /// <param name="directory">The directory to save the data.</param>
        internal static void UnZipFile(SPListItem historyItem, string directory)
        {
            if (!Directory.Exists(directory))
                Directory.CreateDirectory(directory);

            using (ZipInputStream s = new ZipInputStream(historyItem.File.OpenBinaryStream(SPOpenBinaryOptions.SkipVirusScan)))
            {
                ZipEntry theEntry;
                while ((theEntry = s.GetNextEntry()) != null)
                {
                    if (theEntry.Name != String.Empty)
                    {
                        FileStream streamWriter = File.Create(Path.Combine(directory, theEntry.Name));
                        int size = 4096;
                        byte[] data = new byte[size];
                        while (true)
                        {
                            size = s.Read(data, 0, data.Length);
                            if (size > 0)
                            {
                                streamWriter.Write(data, 0, size);
                            }
                            else
                            {
                                break;
                            }
                        }
                        streamWriter.Close();
                    }
                }
                s.Close();
            }
        }

        /// <summary>
        /// Zips the files.
        /// </summary>
        /// <param name="directory">The directory containing the files to zip.</param>
        internal static void ZipFiles(string directory)
        {
            string[] filenames = Directory.GetFiles(directory);

            using (ZipOutputStream s = new ZipOutputStream(File.Create(directory + "\\item.zip")))
            {
                s.SetLevel(9); // 0-9, 9 being the highest compression
                byte[] buffer = new byte[4096];
                foreach (string file in filenames)
                {
                    ZipEntry entry = new ZipEntry(Path.GetFileName(file));
                    entry.DateTime = DateTime.Now;
                    s.PutNextEntry(entry);
                    using (FileStream fs = File.OpenRead(file))
                    {
                        int sourceBytes;
                        do
                        {
                            sourceBytes = fs.Read(buffer, 0, buffer.Length);
                            s.Write(buffer, 0, sourceBytes);
                        }
                        while (sourceBytes > 0);
                    }
                }
                s.Finish();
                s.Close();
            }
        }

    }
}